﻿using ATM.Model;
using ATM.Utilities;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace ATM.Components.Mock
{
    /// <summary>
    /// This is just a mock class, that implements IBank interface so that it is easier to check if the rest of our code complies with the interface.
    /// In real world this class would not be a part of implementation in that way, more likely it would be a client for a remote web service.
    /// </summary>
    class BankMock : IBank
    {
        public Dictionary<string, string> KnownCrediCards { get; private set; }

        public List<string> BlockedCreditCards { get; private set; }

        public Dictionary<string, int> AuthorizationLimits { get; private set; }

        public Dictionary<string, int> PinEntryCount { get; private set; }

        public BankMock()
        {
            KnownCrediCards = new Dictionary<string, string>();
            BlockedCreditCards = new List<string>();
            AuthorizationLimits = new Dictionary<string, int>();
            PinEntryCount = new Dictionary<string, int>();

            // Create some mock values... So we can test the ATM
            KnownCrediCards.Add("1111-1111-1111-1111", "1111");
            KnownCrediCards.Add("2222-2222-2222-2222", "2222");
            KnownCrediCards.Add("3333-4444-5555-6666", "6543");
            KnownCrediCards.Add("1234-4321-5678-8756", "5852");
            KnownCrediCards.Add("6465-8756-325-8444", "9317");

            BlockedCreditCards.Add("9999-9999-9999-9999");
            BlockedCreditCards.Add("2525-2525-2525-2525");

            AuthorizationLimits.Add("1111-1111-1111-1111", 20000);
            AuthorizationLimits.Add("2222-2222-2222-2222", 1000);
            AuthorizationLimits.Add("3333-4444-5555-6666", 500);
            AuthorizationLimits.Add("1234-4321-5678-8756", 500000);
            AuthorizationLimits.Add("6465-8756-325-8444", 2000);
        }

        /// <inheritdoc />
        public bool VerifyConnection()
        {
            // We can assume that bank is always available :)
            return true;
        }

        /// <inheritdoc />
        public CreditCardVerificationStatus VerifyCreditCard(CreditCardInfo creditCardInfo)
        {
            Contract.Assume(creditCardInfo != null);
            Contract.Assume(this.BlockedCreditCards != null);
            Contract.Assume(this.KnownCrediCards != null);

            if (BlockedCreditCards.Contains(creditCardInfo.Number))
            {
                return CreditCardVerificationStatus.BLOCKED;
            }

            return KnownCrediCards.ContainsKey(creditCardInfo.Number) ? CreditCardVerificationStatus.OK : CreditCardVerificationStatus.INVALID;
        }

        /// <inheritdoc />
        public PinCodeVerificationStatus VerifyPinCode(string creditCardNumber, string pinCode)
        {
            Contract.Assume(KnownCrediCards != null);

            string savedPinCode = "";
            KnownCrediCards.TryGetValue(creditCardNumber, out savedPinCode);

            if (savedPinCode.Equals(pinCode))
            {
                PinEntryCount.Remove(creditCardNumber);
                return PinCodeVerificationStatus.OK;
            }
            else
            {
                if (PinEntryCount.ContainsKey(creditCardNumber))
                {
                    PinEntryCount[creditCardNumber] += 1;
                    if (PinEntryCount[creditCardNumber] > 3)
                    {
                        return PinCodeVerificationStatus.FAILED_CARD_LOCKED;
                    }
                }
                else
                {
                    PinEntryCount.Add(creditCardNumber, 1);
                }
                return PinCodeVerificationStatus.FAILED;
            }

        }

        /// <inheritdoc />
        public bool WithdrawAmount(string creditCardNumber, string pinCode, int amount)
        {
            Contract.Assume(AuthorizationLimits != null);

            if (AuthorizationLimits.ContainsKey(creditCardNumber) && VerifyPinCode(creditCardNumber, pinCode) == PinCodeVerificationStatus.OK)
            {
                return AuthorizationLimits[creditCardNumber] <= amount;
            }
            else
            {
                return false;
            }
        }
    }
}
